@using BlazorDatasheet.DataStructures.Geometry
@using BlazorDatasheet.Render
@using BlazorDatasheet.Services
@using BlazorDatasheet.Util
@using Microsoft.JSInterop
@using System.Text
@using BlazorDatasheet.Core.Data
@using BlazorDatasheet.Core.Events.Layout
@using BlazorDatasheet.Core.Interfaces
@using BlazorDatasheet.DataStructures.Intervals
@using BlazorDatasheet.DataStructures.Util
@using BlazorDatasheet.Events
@using BlazorDatasheet.Formula.Core.Interpreter.References
@inject IJSRuntime JS;
@implements IDisposable


<div style="position: absolute; top: 0; left: 0;">

    @foreach (var interval in _selectionIntervals)
    {
        <BoxOverlayRenderer
            BackgroundVisible="@true"
            BorderThickness="0"
            BackgroundStyle="background:var(--selection-bg-color);"
            Width="CellLayoutProvider.ComputeWidth(interval.Start, interval.Length)"
            Height="CellLayoutProvider.ComputeHeight(-1, 1)"
            X="CellLayoutProvider.ComputeLeftPosition(interval.Start)"
            Y="CellLayoutProvider.ComputeTopPosition(-1)"/>
    }

    <!-- Ardoners -->
    @for (int i = VisibleColStart; i < (VisibleColStart + NVisibleCols); i++)
    {
        var col = i;
        <div
            @onmousedown="e => HandleMouseDownOnAdorner(col)"
            style="@GetArdornerSizeStyles(col)
            position:absolute; z-index: 2;  
            background: none; cursor: col-resize; display: block">
        </div>
    }

    <!-- Resize preview -->
    @if (IsDragging)
    {
        var resizeWidth = Math.Max(_minResizeWidth, CellLayoutProvider.ComputeWidth(ActiveAdornerIndex, 1) + CurrentColResizeAmount);
        <BoxOverlayRenderer
            Y="0"
            Width="3"
            BorderThickness="1"
            X="@(CellLayoutProvider.ComputeLeftPosition(ActiveAdornerIndex) + resizeWidth)"
            Height="@(CellLayoutProvider.TotalHeight)"/>
    }

</div>

@for (var i = VisibleColStart; i < (VisibleColStart + NVisibleCols); i++)
{
    var col = i;
    var colHeading = Sheet?.Columns.GetHeading(i) ?? RangeText.ColNumberToLetters(col);

    <div
        class="sheet-cell @GetActiveClass(col) column-head"
        style="z-index: 1; 
        @CssUtil.GetCellWidthStyles(i, 1, CellLayoutProvider); 
        height: @(CellLayoutProvider.ColHeadingHeight)px"
        @onmouseup="e => HandleMouseUp(col, e)"
        @onmouseover="e => HandleMouseOver(col, e)"
        @onmousedown="e => HandleMouseDown(col, e)">
        <div class="cell">
            @colHeading
        </div>
    </div>
}

@code {

    private Sheet? _sheet;

    [Parameter, EditorRequired]
    public Sheet? Sheet { get; set; }

    [Parameter]
    public CellLayoutProvider CellLayoutProvider { get; set; }

    [Parameter]
    public EventCallback<ColumnMouseEventArgs> OnMouseUp { get; set; }

    [Parameter]
    public EventCallback<ColumnMouseEventArgs> OnMouseOver { get; set; }

    [Parameter]
    public EventCallback<ColumnMouseEventArgs> OnMouseDown { get; set; }

    [Parameter]
    public int NVisibleCols { get; set; }

    [Parameter]
    public int VisibleColStart { get; set; }

    private IWindowEventService _windowEventService;

    private double MouseX { get; set; }
    private double MouseXStart { get; set; }
    public int ActiveAdornerIndex { get; set; }
    public bool IsDragging { get; set; }
    public double CurrentColResizeAmount { get; set; }
    private int _minResizeWidth = 15;

    public List<OrderedInterval> _selectionIntervals { get; set; } = new();

    protected override void OnParametersSet()
    {
        if (_sheet != Sheet)
        {
            if (_sheet != null)
            {
                _sheet.Selection.SelectingChanged -= OnSelectingChanged;
                _sheet.Selection.SelectionChanged -= SelectionOnSelectionChanged;;
            }
            _sheet = Sheet;
            if (_sheet == null)
                return;
            
            _sheet.Selection.SelectingChanged += OnSelectingChanged;                
            _sheet.Selection.SelectionChanged += SelectionOnSelectionChanged;;

        }
        base.OnParametersSet();
    }
    
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            _windowEventService = new WindowEventService(JS);
            await _windowEventService.Init();
            _windowEventService.OnMouseUp += WindowEventServiceOnOnMouseUp;
            _windowEventService.OnMouseMove += WindowHandleMouseMove;
        }
    }

    [JSInvokable]
    public Task<bool> WindowHandleMouseMove(MouseEventArgs arg)
    {
        MouseX = arg.PageX;
        if (IsDragging)
        {
            CurrentColResizeAmount = MouseX - MouseXStart; // 10 being the minimum width we can resize to
            StateHasChanged();
            return Task.FromResult(true);
        }
        return Task.FromResult(false);
    }

    private void HandleMouseDownOnAdorner(int col)
    {
        MouseXStart = MouseX;
        ActiveAdornerIndex = col;
        CurrentColResizeAmount = 0;
        IsDragging = true;
    }

    private Task<bool> WindowEventServiceOnOnMouseUp(MouseEventArgs arg)
    {
        if (IsDragging)
        {
            IsDragging = false;
            var resizeWidth = Math.Max(_minResizeWidth, CellLayoutProvider.ComputeWidth(ActiveAdornerIndex, 1) + CurrentColResizeAmount);
            Sheet.Columns.SetWidth(ActiveAdornerIndex, resizeWidth);
            return Task.FromResult(true);
        }

        return Task.FromResult(false);
    }

    public void Dispose()
    {
        _windowEventService?.Dispose();
    }

    private bool IsActive(int col)
    {
        return (Sheet!.Selection!.SelectingRegion?.SpansCol(col) == true)
               || Sheet!.Selection!.Regions.Any(x => x.SpansCol(col));
    }

    private void OnSelectingChanged(object? sender, IRegion region) => UpdateIntervals();

    private void SelectionOnSelectionChanged(object? sender, IEnumerable<IRegion> e) => UpdateIntervals();

    private void UpdateIntervals()
    {
        StateHasChanged();
    }

    private void HandleMouseUp(int col, MouseEventArgs args)
    {
        OnMouseUp.InvokeAsync(new ColumnMouseEventArgs(col, args));
    }

    private void HandleMouseDown(int col, MouseEventArgs args)
    {
        OnMouseDown.InvokeAsync(new ColumnMouseEventArgs(col, args));
    }

    private void HandleMouseOver(int col, MouseEventArgs args)
    {
        OnMouseOver.InvokeAsync(new ColumnMouseEventArgs(col, args));
    }

    private string GetArdornerSizeStyles(int col)
    {
        var sb = new StringBuilder();
        var left = CellLayoutProvider.ComputeLeftPosition(col + 1);
        var top = 0;
        var width = 10;
        var height = CellLayoutProvider.ColHeadingHeight;

        sb.Append($"top:{top}px;");
        sb.Append($"left:{left - 6}px;");
        sb.Append($"width:{width}px;");
        sb.Append($"height:{height}px;");
        return sb.ToString();
    }

    private string GetActiveClass(int col)
    {
        var isSelecting = (Sheet!.Selection!.SelectingRegion?.SpansCol(col) == true)
                          || Sheet!.Selection!.Regions.Any(x => x.SpansCol(col));
        return isSelecting ? "column-active" : "";
    }

}